package unittests;

import java.io.IOException;
import java.io.StringBufferInputStream;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import util.RewindableInputStream;

import fterms.BaseOntology;
import fterms.FTKBase;
import fterms.FTRefinement;
import fterms.FTUnification;
import fterms.FeatureTerm;
import fterms.FloatFeatureTerm;
import fterms.IntegerFeatureTerm;
import fterms.NOOSParser;
import fterms.Ontology;
import fterms.SetFeatureTerm;
import fterms.Sort;
import fterms.Symbol;
import fterms.SymbolFeatureTerm;
import fterms.TermFeatureTerm;
import fterms.exceptions.FeatureTermException;


public class FTermTests {
	public static void main(String args[]) {
		long t_start,t_end;
		int errors = 0;

		System.out.println("Feature Terms Unit Tests...");

		try {
			t_start=System.currentTimeMillis();
			errors+=basicTests();
			t_end=System.currentTimeMillis();
			System.out.println("Time: " + (t_end - t_start));
		} catch (Exception e) {
			e.printStackTrace();
		}

		System.out.println("Feature Terms Unit Tests finished: " + errors + " tests failed.");
	}

	static int basicTests() throws IOException, FeatureTermException
	{
		int errors = 0;

		Ontology base_ontology = new BaseOntology();
		Ontology o = new Ontology();
		o.uses(base_ontology);
		FTKBase base_domain_model = new FTKBase();
		FTKBase domain_model_trains = new FTKBase();
		FTKBase domain_model_toxicology = new FTKBase();
		FTKBase domain_model_families = new FTKBase();

		/*
		 * ---------------------------------------------------------------------------------
		 */

		System.out.println("Initialization tests...");
		if (base_ontology.getNSorts()!=6) {
			int n = base_ontology.getNSorts();
			System.out.println("Initialization Test 1 failed: N sorts in base_ontology is " + n);
			for(Sort s:base_ontology.getSorts()) {
				System.out.println(s.get());
			}
			errors++;
		}
		if (base_ontology.getNUndefinedSorts()!=0) {
			int n = base_ontology.getNUndefinedSorts();
			System.out.println("Initialization Test 2 failed: N undefined sorts in base_ontology is " + n);
			for(Sort s:base_ontology.getSorts()) {
				System.out.println(s.get());
			}
			errors++;
		}

		base_domain_model.create_boolean_objects(base_ontology);
		domain_model_trains.uses(base_domain_model);
		domain_model_toxicology.uses(base_domain_model);
		domain_model_families.uses(base_domain_model);

		if (base_ontology.getNSorts()!=6) {
			int n = base_ontology.getNSorts();
			System.out.println("Initialization Test 3 failed: N sorts in base_ontology is " + n);
			for(Sort s:base_ontology.getSorts()) {
				System.out.println(s.get());
			}
			errors++;
		}
		if (base_domain_model.get_n_terms()!=2) {
			System.out.println("Initialization Test 4 failed: N terms in base_domain_model is " + base_domain_model.get_n_terms());
			errors++;
		}
		if (base_domain_model.get_n_undefined_terms()!=0) {
			System.out.println("Initialization Test 5 failed: N undefined terms in base_domain_model is " + base_domain_model.get_n_undefined_terms());
			errors++;
		}

		/*
		 * ---------------------------------------------------------------------------------
		 */

		System.out.println("Ontology Load tests...");
		domain_model_trains.ImportNOOS("NOOS/trains-ontology.noos",o);
		if (o.getNSorts()!=10) {
			int n = o.getNSorts();
			System.out.println("Ontology Test 1 failed: N sorts in o is " + n);
			for(Sort s:o.getSorts()) {
				System.out.println(s.get());
			}
			errors++;
		}
		domain_model_toxicology.ImportNOOS("NOOS/toxic-eva-ontology.noos",o);
		if (o.getNSorts()!=230) {
			int n = o.getNSorts();
			System.out.println("Ontology Test 2 failed: N sorts in o is " + n);
			System.out.println(o.getDescription());
			errors++;
		}

		/*
		 * ---------------------------------------------------------------------------------
		 */

		System.out.println("Case Base Load tests...");
		FTKBase cb_trains = new FTKBase();
		domain_model_trains.ImportNOOS("NOOS/trains-dm.noos", o);
		cb_trains.uses(domain_model_trains);
		cb_trains.ImportNOOS("NOOS/trains-cases-10.noos", o);

		cb_trains.print_status();

		if (cb_trains.SearchFT(o.getSort("trains-description")).size()!=10) {
			int n = cb_trains.SearchFT(o.getSort("trains-description")).size();
			System.out.println("Case Base Load Test 1 failed: N trains-description objects is " + n);
			for(FeatureTerm obj:cb_trains.SearchFT(o.getSort("trains-description"))) {
				System.out.println(obj.toStringNOOS(domain_model_trains));
			}
			errors++;
		}

		FTKBase cb_toxicology = new FTKBase();
		cb_toxicology.uses(domain_model_toxicology);
		domain_model_toxicology.ImportNOOS("NOOS/toxic-eva-dm.noos", o);
		cb_toxicology.ImportNOOS("NOOS/toxic-eva-cases-50.noos", o);

		cb_toxicology.print_status();

		if (cb_toxicology.SearchFT(o.getSort("toxic-problem")).size()!=47) {
			int n = cb_toxicology.SearchFT(o.getSort("toxic-problem")).size();
			System.out.println("Case Base Load Test 2 failed: N toxic-problem objects is " + n);
			for(FeatureTerm f:cb_toxicology.SearchFT(o.getSort("toxic-problem"))) {
				if (f.getName()!=null) {
					System.out.println(f.getName().get());
				} else {
					System.out.println(f.toStringNOOS(domain_model_toxicology));
				}
			}
			errors++;
		}

		/*
		 * ---------------------------------------------------------------------------------
		 */

		System.out.println("Subsumtion tests...");
		{
			FTKBase case_base = new FTKBase();
			case_base.uses(base_domain_model);
			FeatureTerm f1 = NOOSParser.parse(new RewindableInputStream(new StringBufferInputStream("(define (symbol))")),case_base,o);
			FeatureTerm f2 = NOOSParser.parse(new RewindableInputStream(new StringBufferInputStream("\"hello\"")),case_base,o);
			FeatureTerm f3 = NOOSParser.parse(new RewindableInputStream(new StringBufferInputStream("\"bye\"")),case_base,o);
			FeatureTerm f4 = NOOSParser.parse(new RewindableInputStream(new StringBufferInputStream("\"hello\"")),case_base,o);

			if (!f1.subsumes(f2)) {
				errors++;
				System.out.println("a generic Symbol does not subsume a constant symbol!");
			}

			if (!f2.subsumes(f4)) {
				errors++;
				System.out.println("Two identical constant symbols do not subsume each other!");
			}

			if (f2.subsumes(f3)) {
				errors++;
				System.out.println("Two different constant symbols subsume each other!");
			}
			
			if (!f2.equals(f4)) {
				errors++;
				System.out.println("Two identical constant symbols are not equals!");				
			}

			if (f2.equals(f3)) {
				errors++;
				System.out.println("Two different constant symbols are equals!");				
			}
			
			List<FeatureTerm> l = new LinkedList<FeatureTerm>();
			HashSet<FeatureTerm> s = new HashSet<FeatureTerm>();
			
			if (!l.contains(f1)) l.add(f1);
			if (!l.contains(f2)) l.add(f2);
			if (!l.contains(f3)) l.add(f3);
			if (!l.contains(f4)) l.add(f4);
			if (l.size()!=3) {
				errors++;
				System.out.println("List containment failure!");								
			}

			if (!s.contains(f1)) s.add(f1);
			if (!s.contains(f2)) s.add(f2);
			if (!s.contains(f3)) s.add(f3);
			if (!s.contains(f4)) s.add(f4);
			if (s.size()!=3) {
				errors++;
				System.out.println("HashSet containment failure!");								
			}
			
			
			{
				// Problem in sponge subsumption:
				Ontology o2=new Ontology();
				FTKBase dm=new FTKBase();
				o2.uses(base_ontology);
				FTKBase case_base2=new FTKBase();
				case_base2.uses(dm);

				dm.create_boolean_objects(o2);

				dm.ImportNOOS("NOOS/sponge-ontology.noos",o2);
				dm.ImportNOOS("NOOS/sponge-dm.noos",o2);

				f1 = NOOSParser.parse(new RewindableInputStream(new StringBufferInputStream("(define (megascleres) (acanthose (define (acanthose))))")),case_base2,o2);
				f2 = NOOSParser.parse(new RewindableInputStream(new StringBufferInputStream("(define (megascleres) (acanthose no-acanthose))")),case_base2,o2);
			
				if (!f1.subsumes(f2)) {
					errors++;
					System.out.println("Sponge subsumption failure 1!!");						
				}
				if (f2.subsumes(f1)) {
					errors++;
					System.out.println("Sponge subsumption failure 2!!");						
				}
			
			}
		}

		/*
		 * ---------------------------------------------------------------------------------
		 */

		System.out.println("Specialization Refinements tests...");
		{
			base_domain_model.ImportNOOS("NOOS/simple-family-ontology.noos",o);

			TermFeatureTerm x1 = new TermFeatureTerm((Symbol)null,o.getSort("person"));
			FeatureTerm x2 = new TermFeatureTerm((Symbol)null,o.getSort("male"));
			FeatureTerm x3 = new TermFeatureTerm((Symbol)null,o.getSort("female"));
			FeatureTerm x4 = new TermFeatureTerm((Symbol)null,o.getSort("person"));
			SetFeatureTerm set1 = new SetFeatureTerm();
			set1.addSetValue(x3);
			set1.addSetValue(x4);
			x1.defineFeatureValue(new Symbol("father"),x2);
			x1.defineFeatureValue(new Symbol("children"),set1);

			System.out.println("Generated Term:\n" + x1.toStringNOOS());

			Set<FeatureTerm> variables = FTRefinement.variables(x1);
			if (variables.size()!=4) {
				System.out.println("Specialization Refinements Test 1 failed: N variables is " + variables.size());
				for(FeatureTerm f:variables) {
					if (f.getName()!=null) {
						System.out.println(f.getName().get());
					} else {
						System.out.println(f.toStringNOOS(base_domain_model));
					}
				}
				errors++;
			}

			List<FeatureTerm> sortRefinements = FTRefinement.sortSpecialization(x1,base_domain_model,null);
			if (sortRefinements.size()!=4) {
				System.out.println("Specialization Refinements Test 2 failed: N sort refinements is " + sortRefinements.size());
				for(FeatureTerm f:sortRefinements) {
					System.out.println(f.toStringNOOS(base_domain_model));
				}
				errors++;
			}

			List<FeatureTerm> featureRefinements = FTRefinement.featureIntroduction(x1,base_domain_model,null);
			if (featureRefinements.size()!=20) {
				System.out.println("Specialization Refinements Test 3 failed: N feature refinements is " + featureRefinements.size());
				for(FeatureTerm f:featureRefinements) {
					System.out.println(f.toStringNOOS(base_domain_model));
				}
				errors++;
			}

			List<FeatureTerm> equalityRefinements = FTRefinement.variableEqualityAddition(x1,base_domain_model,null);
			if (equalityRefinements.size()!=2) {
				System.out.println("Specialization Refinements Test 4 failed: N equality refinements is " + equalityRefinements.size());
				for(FeatureTerm f:equalityRefinements) {
					System.out.println(f.toStringNOOS(base_domain_model));
				}
				errors++;
			}

			List<FeatureTerm> setRefinements = FTRefinement.setExpansion(x1,base_domain_model,null);
			if (setRefinements.size()!=2) {
				System.out.println("Specialization Refinements Test 5 failed: N set refinements is " + setRefinements.size());
				for(FeatureTerm f:setRefinements) {
					System.out.println(f.toStringNOOS(base_domain_model));
				}
				errors++;
			}
		}

		/*
		 * ---------------------------------------------------------------------------------
		 */

		System.out.println("Generalization Refinements tests...");
		{
			domain_model_families.ImportNOOS("NOOS/family-ontology.noos", o);
			domain_model_families.ImportNOOS("NOOS/family-dm.noos", o);
			FTKBase cb_families = new FTKBase();
			cb_families.uses(domain_model_families);
			cb_families.ImportNOOS("NOOS/family-cases-test.noos", o);

			FeatureTerm root1,root2;
			{
				Set<FeatureTerm> l = cb_families.SearchFT(new Symbol("victoria")); 
				if (l.size()!=1) {
					System.out.println("Generalization Refinements Test 1 failed: There is more than 1 roberto in the case base!");
					errors++;
				}
				root1 = l.iterator().next();
			}
			{
				Set<FeatureTerm> l = cb_trains.SearchFT(new Symbol("t1")); 
				if (l.size()!=1) {
					System.out.println("Generalization Refinements Test 2 failed: There is more than 1 roberto in the case base!");
					errors++;
				}
				root2 = l.iterator().next();
			}

			System.out.println("Generated Term Root 1:\n" + root1.toStringNOOS());			
			List<FeatureTerm> generalizations1 = FTRefinement.getGeneralizations(root1, domain_model_families, o);
			System.out.println(generalizations1.size() + " generalizations");
			if (generalizations1.size()!=9) {
				System.out.println("Generalization Refinements Test 3 failed: not 9 generalizations of victoria!");
				errors++;				
			}
			for(FeatureTerm g:generalizations1) {
				if (!g.subsumes(root1)) {
					System.out.println("Generalization Refinements Test 4 failed: Generalization does not subsume original!");
					System.out.println(g.toStringNOOS());
					errors++;
				}
			}

			System.out.println("Generated Term Root 2:\n" + root2.toStringNOOS());
			List<FeatureTerm> generalizations2 = FTRefinement.getGeneralizations(root2, domain_model_trains, o);			
			System.out.println(generalizations2.size() + " generalizations");
			if (generalizations1.size()!=9) {
				System.out.println("Generalization Refinements Test 5 failed: not 9 generalizations of T1!");
				errors++;				
			}
			for(FeatureTerm g:generalizations2) {
				if (!g.subsumes(root2)) {
					System.out.println("Generalization Refinements Test 6 failed: Generalization does not subsume original!");
					System.out.println(g.toStringNOOS());
					errors++;
				}
			}


		}

		/*
		 * ---------------------------------------------------------------------------------
		 */

		System.out.println("Antiunification tests...");
		// ...

		/*
		 * ---------------------------------------------------------------------------------
		 */

		System.out.println("Generalization tests...");
		// ...


		errors += unificationTests(o,domain_model_families);

		return errors;
	}


	static int unificationTests(Ontology o,FTKBase dm) throws FeatureTermException, IOException 
	{
		int errors = 0;
		int i = 0;
		FTKBase case_base = new FTKBase();

		case_base.uses(dm);

        dm.ImportNOOS("NOOS/zoology-ontology.noos",o);
        dm.ImportNOOS("NOOS/zoology-dm.noos",o);
        dm.ImportNOOS("NOOS/sponge-ontology.noos",o);
        dm.ImportNOOS("NOOS/sponge-dm.noos",o);

        System.out.println("Unification tests...");

		FeatureTerm f1 = null;
		FeatureTerm f2 = null;
		int expected_result = 0;
		List<FeatureTerm> unifications;

		//	Integers:
		do {
			switch(i) {
			case 0:
				f1 = new IntegerFeatureTerm(null,o);
				f2 = new IntegerFeatureTerm(1,o);
				expected_result = 1;
				break;
			case 1:
				f1 = new FloatFeatureTerm(null,o);
				f2 = new FloatFeatureTerm(1.0f,o);
				expected_result = 1;
				break;
			case 2:
				f1 = new SymbolFeatureTerm(null,o);
				f2 = new SymbolFeatureTerm(new Symbol("hola"),o);
				expected_result = 1;
				break;	
			case 3:

				f1 = NOOSParser.parse(new RewindableInputStream(new StringBufferInputStream(
						"(define (person) " +
						"  (father (define (male)))" +
				")")),case_base,o);
				f2 = NOOSParser.parse(new RewindableInputStream(new StringBufferInputStream(
						"(define (female) " + 
						"  (father (define (person)))" +
				")")),case_base,o);
				expected_result = 1;			 
				break;
			case 4:
				f1 = NOOSParser.parse(new RewindableInputStream(new StringBufferInputStream(
						"(define (person) " +
						"  (son (define (male)))" +
				")")),case_base,o);
				f2 = NOOSParser.parse(new RewindableInputStream(new StringBufferInputStream(
						"(define (female) " +
						"  (son (define (person) (son (define (male))))))" + 
				")")),case_base,o);
				expected_result = 2;
				break;
			case 5:				
				f1 = NOOSParser.parse(new RewindableInputStream(new StringBufferInputStream(
						"(define (person) " +
						"  (son (define (male) " +
						"         (wife (define (female)) )) " +
						"       (define (male)))" +
				")")),case_base,o);
				f2 = NOOSParser.parse(new RewindableInputStream(new StringBufferInputStream(
						"(define (female) " +
						"  (son (define (person) " +
						"         (son (define (male)))))" +
				")")),case_base,o);
				expected_result = 3;
				break;
			case 6:				
				f1 = NOOSParser.parse(new RewindableInputStream(new StringBufferInputStream(
						"(define ?X1 (male) " +
									   "  (son (define (male) " +
									   "         (father !X1)) " +
									   "  ) " +
									   ") ")),case_base,o);
				f2 = NOOSParser.parse(new RewindableInputStream(new StringBufferInputStream(
						"(define (male) " +
									   ") ")),case_base,o);
				expected_result = 1;
				break;
			case 7:
				f1 = NOOSParser.parse(new RewindableInputStream(new StringBufferInputStream(
						"(define ?X1 (male) " +
									   "  (son (define (male) " +
									   "         (wife (define (female))) " +
									   "         (father !X1)) " +
									   "  ) " +
									   ") ")),case_base,o);
				f2 = NOOSParser.parse(new RewindableInputStream(new StringBufferInputStream(
						"(define (male) " +
									   "  (son (define (male) " +
									   "         (father (define (male)))) " +
									   "  ) " +
									   ") ")),case_base,o);
				expected_result = 1;
				break;
			case 8:
				f1 = NOOSParser.parse(new RewindableInputStream(new StringBufferInputStream(
						"(define (person) " +
									   "  (son (define ?X1 (male))) " +
									   "  (father !X1) " +
									   ") ")),case_base,o);
					f2 = NOOSParser.parse(new RewindableInputStream(new StringBufferInputStream(
							"(define (male) " +
									   "  (son (define (male) " +
									   "         (uncle (define (male))))) " +
									   "  (father (define (male) " +
									   "            (mother (define (female))))) " +
									   ") ")),case_base,o);
				expected_result = 4;
				break;
			case 9:
				f1 = NOOSParser.parse(new RewindableInputStream(new StringBufferInputStream(
						"(define (person) " +
											       "  (son (define (male))) " +
												   ") ")),case_base,o);
				f2 = NOOSParser.parse(new RewindableInputStream(new StringBufferInputStream(
						"(define (male) " +
												   "  (son (define (set))) " +
												   "  (father (define (set))) " +
												   ") ")),case_base,o);
				expected_result = 1;
				break;
            case 10:
                f1 = NOOSParser.parse(new RewindableInputStream(new StringBufferInputStream(
                        "(define (sponge-problem) " +
                        " (description (define (sponge) " +
                        " (spiculate-skeleton (define (spiculate-skeleton) " +
                        " (megascleres (define (megascleres) " +
                        " (smooth-form style))))))))")),case_base,o);
                f2 = NOOSParser.parse(new RewindableInputStream(new StringBufferInputStream(
                        "(define (sponge-problem) " +
                        " (description (define (sponge) " +
                        " (spiculate-skeleton (define (spiculate-skeleton) " +
                        " (megascleres (define (megascleres) " +
                        " (smooth-form oxea))))))))")),case_base,o);
				expected_result = 1;
                break;
			default:
				f1 = null;
                f2 = null;
			break;
			}

			if (f1!=null) {
				System.out.println("F1:\n" + f1.toStringNOOS(dm));
				System.out.println("F2:\n" + f2.toStringNOOS(dm));

				unifications=FTUnification.unification(f1,f2,o,dm, true);
				if (unifications!=null && !unifications.isEmpty()) {
					System.out.println("Unification successful!");
					System.out.println(unifications.size() + " unifications");
					for(FeatureTerm u:unifications) {						
						System.out.println(u.toStringNOOS(dm));
						if (!f1.subsumes(u)) {
							System.out.println("ERROR! f1 does not subsume u!");
							errors++;
						}
						if (!f2.subsumes(u)) {
							System.out.println("ERROR! f2 does not subsume u!");
							errors++;
						}
					}
					if (expected_result!=unifications.size()) {
						errors++;
						System.out.println("ERROR! " + expected_result + " expected unifications!");
					}
				} else {
					System.out.println("0 unifications");
					if (expected_result!=0) {
						errors++;
						System.out.println("ERROR! " + expected_result + " expected unifications!");
					}
				} // if 
			}
			i++;
		} while (f1!=null);

		return errors;
	}
}
